package com.lagou.filter;

import com.lagou.bean.MethodInfo;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter, Runnable {
    private Map<String, List<MethodInfo>> methodTimes = new ConcurrentHashMap<>(16);

    public TPMonitorFilter() {
        // 每个5秒打印线程使用情况
        Executors.newSingleThreadScheduledExecutor()
                .scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);

        // 每个1分钟清扫数据
        Thread removeThread = new Thread(new RemoveDataThread(methodTimes));
        Executors.newSingleThreadScheduledExecutor()
                .scheduleWithFixedDelay(removeThread, 10, 60, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //System.out.println("---->TPMonitorFilter");
        // 获取执行方法名
        Result result = null;
        Long takeTime = 0L;
        try {
            long startTime = System.currentTimeMillis();
            result = invoker.invoke(invocation);
            if (result.getException() instanceof Exception) {
                throw new Exception(result.getException());
            }
            takeTime = System.currentTimeMillis() - startTime;
        } catch (Exception e) {
            e.printStackTrace();
        }
        String methodName = invocation.getMethodName();
        List<MethodInfo> methodInfos = methodTimes.get(methodName);
        if (methodInfos == null) {
            methodInfos = new ArrayList<>(1000);
            methodTimes.put(methodName, methodInfos);
        }
        // 添加方法信息
        methodInfos.add(new MethodInfo(invocation.getMethodName(), takeTime, System.currentTimeMillis()));

        return result;
    }

    // rate 代表百分比 90 传入 0.9 即可 99 就是 0.99
    public long getTp(List<MethodInfo> methodInfos, double rate) {
        // 构建一个临时数据保存 满足1一分钟之内的数据
        List<MethodInfo> sortList = new ArrayList<>();
        // 计算最近一分钟的 TP90和TP99
        long endTime = System.currentTimeMillis();
        long startTime = System.currentTimeMillis() - 60000;
        // 遍历列表集合
        int length = methodInfos.size();
        for (int i = 0; i < length; i++) {
            MethodInfo methodInfo = methodInfos.get(i);
            if (methodInfo.getEndTimes() >= startTime && methodInfo.getEndTimes() <= endTime) {
                sortList.add(methodInfo);
            }
        }
        // 排序
        sortList.sort(new Comparator<MethodInfo>() {
            @Override
            public int compare(MethodInfo o1, MethodInfo o2) {
                if (o1.getTimes() > o2.getTimes()) {
                    return 1;
                } else if (o1.getTimes() < o2.getTimes()) {
                    return -1;
                }
                return 0;
            }
        });
        // 获取TP下标
        int index = (int) (sortList.size() * rate);

        return sortList.get(index).getTimes();
    }

    @Override
    public void run() {
        // 使用计算TP数据
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

        System.out.println("------------------->计算TP数据<-------------------");
        // 遍历数据
        for (String key : methodTimes.keySet()) {
            List<MethodInfo> methodInfos = methodTimes.get(key);

            // 获取TP数据
            long tp90 = getTp(methodInfos, 0.90D);
            long tp99 = getTp(methodInfos, 0.99D);

            String tp_str = sdf.format(date) + " " + key +
                    " 方法的TP90:" + tp90 + "毫秒，TP99:" + tp99 + "毫秒";
            System.out.println(tp_str);
        }
    }

}
